/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Ucop/Util/XmlConfiguration.h"


namespace Ucop {
namespace Util {

    XmlConfiguration::XmlConfiguration(void)
        : _Mydata()
    {

    }

    XmlConfiguration::~XmlConfiguration(void)
    {

    }

    bool XmlConfiguration::Open(const char *_Filename)
    {
        TiXmlDocument _Doc;
        if (!_Doc.LoadFile(_Filename, TIXML_DEFAULT_ENCODING)) {
            return false;
        }

        for (TiXmlNode* _ChildEl = _Doc.RootElement()->FirstChildElement();
            _ChildEl != NULL; _ChildEl = _ChildEl->NextSiblingElement())
        {
            Section _Section;
            _Section.Key = _ChildEl->Value();
            _BuildData(_ChildEl, _Section);
            _Mydata.push_back(_Section);
        }

        return true;
    }

    void XmlConfiguration::_BuildData(TiXmlNode* _Node, struct Section& _Section)
    {
        for (TiXmlNode *_ChildNode = _Node->FirstChild(); _ChildNode; _ChildNode = _ChildNode->NextSibling())
        {
            switch (_ChildNode->Type())
            {
            case TiXmlNode::TINYXML_ELEMENT:
                TiXmlNode *_FirstChild = _ChildNode->FirstChild();
                TiXmlNode *_LastChild = _ChildNode->LastChild();
                if ((_FirstChild != NULL)
                    && (_FirstChild == _LastChild)
                    && (_FirstChild->ToText())) {
                    SectionKV kv;
                    kv.Key = _ChildNode->Value();
                    kv.Value = _FirstChild->ToText()->Value();
                    _Section.Value.push_back(kv);
                } else {
                    Section subSec;
                    subSec.Key = _ChildNode->Value();
                    _BuildData(_ChildNode, subSec);
                    if (!(subSec.Value.empty() && subSec.Children.empty())) {
                        _Section.Children.push_back(subSec);
                    }
                }
                break;
            }
        }
    }

    struct Configuration::Section* XmlConfiguration::_FindSection(Tokens& _Tokens)
    {
        Tokens::const_iterator _Iter = _Tokens.begin();
        if (_Iter == _Tokens.end()) {
            return NULL;
        }

        struct Section* _FirstNode = NULL;
        for (std::vector<struct Section>::size_type _Index = 0; 
            _Index < _Mydata.size(); ++_Index)
        {
            if (_Mydata[_Index].Key == _Iter->c_str()) {
                _FirstNode = &_Mydata[_Index];
                break;
            }
        }
        _Tokens.erase(_Tokens.begin());

        if (_Tokens.empty()) {
            return _FirstNode;
        }
        return _FirstNode == NULL ? NULL : _FindSection(_FirstNode, _Tokens);
    }

    struct Configuration::Section* XmlConfiguration::_FindSection(struct Section* _Node, Tokens& _Tokens)
    {
        if (_Node == NULL || _Tokens.empty()) {
            return NULL;
        }

        struct Section* _ChildSection = NULL;

        for (std::vector<struct Section>::size_type _Index = 0; 
            _Index < _Node->Children.size(); ++_Index)
        {
            if (_Node->Children[_Index].Key == _Tokens[0].c_str()) {
                _ChildSection = &(_Node->Children[_Index]);
                break;
            }
        }
        _Tokens.erase(_Tokens.begin());

        if (_Tokens.empty()) {
            return _ChildSection;
        }
        
        return _ChildSection == NULL ? NULL : _FindSection(_ChildSection, _Tokens);
    }

    struct Configuration::SectionKV* XmlConfiguration::_FindKV(const char* _Path)
    {
        Tokens _Tokens = StrSplit(_Path, ".");
        if (_Tokens.size() <= 1) {
            return NULL;
        }

        std::string _K = _Tokens[_Tokens.size() - 1];
        _Tokens.pop_back();

        struct Section* _Section = _FindSection(_Tokens);
        if (_Section != NULL)
        {
            for (std::vector<struct SectionKV>::size_type _Index = 0;
                _Index < _Section->Value.size(); ++_Index) {
                if (_Section->Value[_Index].Key == _K) {
                    return &(_Section->Value[_Index]);
                }
            }
        }

        return NULL;
    }

    const char* XmlConfiguration::GetString(const char *_Path, const char *_Default )
    {
        struct SectionKV* kv = _FindKV(_Path);
        if (kv) {
            return kv->Value.c_str();
        }
        return _Default;
    }

    int XmlConfiguration::GetInt(const char *_Path, int _Default /* = 0 */)
    {
        struct SectionKV* kv = _FindKV(_Path);
        if (kv) {
            return atoi(kv->Value.c_str());
        }
        return _Default;
    }

    long XmlConfiguration::GetLong(const char *_Path, long _Default /* = 0L */)
    {
        struct SectionKV* kv = _FindKV(_Path);
        if (kv) {
            return atol(kv->Value.c_str());
        }
        return _Default;
    }

    double XmlConfiguration::GetDouble(const char *_Path, double _Default /* = 0.0 */)
    {
        struct SectionKV* kv = _FindKV(_Path);
        if (kv) {
            return atof(kv->Value.c_str());
        }
        return _Default;
    }

    bool XmlConfiguration::GetBool(const char *_Path, bool _Default /* = false */)
    {
        struct SectionKV* kv = _FindKV(_Path);
        if (kv) {
            return !(kv->Value.empty() || atoi(kv->Value.c_str()) == 0);
        }
        return _Default;
    }

    struct Configuration::Section* XmlConfiguration::GetSection(const char *_Path)
    {
        Tokens _Tokens = StrSplit(_Path, ".");
        return _FindSection(_Tokens);
    }
}}